package com.redhat.redwork.coc;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.config.BeanSelectionProvider;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.json.JSONResult;
import org.apache.struts2.views.freemarker.FreemarkerResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig.Builder;
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
import com.opensymphony.xwork2.inject.Inject;
import com.redhat.redwork.coc.annotation.JSON;

/**
 * 
 * @author Justin
 *
 */
public class RedworkEngine {

	private static final Logger LOG = LoggerFactory.getLogger(RedworkEngine.class);
	
	protected Configuration configuration;
	protected ObjectFactory objectFactory;
	protected String basePackage;
	protected String baseDir;
	protected String parentPackage;
	protected String checkInterface;
	
	@Inject
	public RedworkEngine(
			Configuration configuration,
			ObjectFactory objectFactory,
			@Inject("redwork.parentPackage") String parentPackage,
			@Inject("redwork.basePackage") String basePackage,
			@Inject("redwork.baseDir") String baseDir,
			@Inject("redwork.checkInterface") String checkInterface){
		
		this.configuration = configuration;
		this.objectFactory = objectFactory;
		this.parentPackage = parentPackage;
		this.basePackage = basePackage;
		this.baseDir = baseDir;
		this.checkInterface = checkInterface;
	}
	
	/**
	 * 是否标注了JSON
	 * @param clazzName
	 * @param methodName
	 * @return
	 */
	public boolean jsonAnnotationPresents(ActionConfig actionConfig){
		
		final String clazzName = actionConfig.getClassName();
		final String methodName = actionConfig.getMethodName();
		
		try {
			Class<?> actionClass = Class.forName(clazzName);
			Method actionMethod = actionClass.getMethod(methodName, new Class[]{});
			
			boolean jsonPresents = actionMethod.isAnnotationPresent(JSON.class);
			return jsonPresents;
		} catch (Throwable e) {
			throw new XWorkException(e);
		}
	}
	/**
	 * 创建JSON结果类型
	 * @param actionConfig 需要提取参数actionName methodName
	 * @return
	 */
	public Result builderJsonResult(ActionConfig actionConfig){
		
		final String clazzName = actionConfig.getClassName();
		final String methodName = actionConfig.getMethodName();
		
		// 根对象
		String root = null;
		
		// 排除属性
		String[] includes = {};
		String[] excludes = {};
		
		try {
			Class<?> actionClass = Class.forName(clazzName);
			Method actionMethod = actionClass.getMethod(methodName, new Class[]{});
			boolean jsonPresents = actionMethod.isAnnotationPresent(JSON.class);
			if(jsonPresents){
				JSON json = actionMethod.getAnnotation(JSON.class);
				
				// 提取值
				root = json.root();
				includes = json.includeProperties();
				excludes = json.excludeProperties();
			}	
		} catch (Throwable e) {
			throw new XWorkException(e);
		}
		
		// the builder
		Builder builder = new ResultConfig.Builder("json", JSONResult.class.getName());
		if(root != null && !"".equals(root)){
			builder.addParam("root", root);
		}
		if(includes != null && includes.length > 0){
			builder.addParam("includeProperties", StringUtils.join(includes, ','));
		}
		if(excludes != null && excludes.length > 0){
			builder.addParam("excludeProperties", StringUtils.join(excludes, ','));
		}	
			
		ResultConfig jsonResultCfg = builder.build();
		try {
			return objectFactory.buildResult(jsonResultCfg, ActionContext.getContext().getContextMap());
		} catch (Exception e) {
			throw new XWorkException("Unable to build redwork json result", e, jsonResultCfg);
		}
	}
	
	/**
	 * 创建Freemarker结果类型
	 * @param actionContext
	 * @param actionConfig
	 * @param resultCode
	 * @return
	 */
	public Result buildFreemarkerResult(ActionContext actionContext, ActionConfig actionConfig, String resultCode){
		// build the freemarker result config
		final String packageName = actionConfig.getPackageName().toLowerCase();
		StringBuilder sb = new StringBuilder().append(baseDir);
		if(!resultCode.startsWith("/")){
			sb.append(packageName).append("/");
		}
		sb.append(resultCode).append(".ftl");
		final String pageLocation = sb.toString();
		ResultConfig genericFreemarkerResult = new ResultConfig.Builder(resultCode, FreemarkerResult.class.getName())
		.addParam("location", pageLocation).build();

		try {
			return objectFactory.buildResult(genericFreemarkerResult, actionContext.getContextMap());
		} catch (Exception e) {
			throw new XWorkException("Unable to build redwork freemarker result", e, genericFreemarkerResult);
		}
	}
	
	/**
	 * well, where you from... back to referer
	 * @return
	 */
	public String getReferer(){
		HttpServletRequest request = ServletActionContext.getRequest();
		
		// 请求头Accept为json
		String referer = request.getHeader("Referer");
		if(referer == null){
			return null;
		}
		
//		String contextPath = request.getContextPath();
		String serverName = request.getServerName();
		int idxServerNameLast = referer.indexOf(serverName) + serverName.length();
		int slashAfterServerAndPort = referer.indexOf("/", idxServerNameLast);
		final String substring = referer.substring(slashAfterServerAndPort);//.substring(contextPath.length());
		// eg. /securityStrategy/management.action
		
		return substring;
	}
	
	/**
	 * well, where you from...
	 * @return 
	 * @return
	 */
	public ActionMapping getActionMappingFromReferer(){
		
		HttpServletRequest request = ServletActionContext.getRequest();
		String contextPath = request.getContextPath();
		
		final String uri = getReferer();
		final String path = uri.substring(contextPath.length());
		
		HttpServletRequest req = new StrutsRequestWrapper(request){
			@Override
			public String getRequestURI() {
				return uri;
			}
			@Override
			public String getServletPath() {
				return path;
			}
		};
		
		ConfigurationManager cm = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
		ActionMapper actionMapper = configuration.getContainer().getInstance(ActionMapper.class);
		ActionMapping mapping = actionMapper.getMapping(req, cm);
		
		return mapping;
	}
	
	/**
	 * 取默认拦截器（栈），先取当前包的拦截器（栈），如果没定义，则取默认包的拦截器（栈）
	 * @param packageName
	 * @return
	 */
	public List<InterceptorMapping> getDefaultIntercepterStack(String packageName){
		
		// 取包配置
		PackageConfig pkgCfg = configuration.getPackageConfig(packageName);
		
		if(pkgCfg == null){
			pkgCfg = configuration.getPackageConfig(parentPackage);
		}
		
		String defaultInterceptorRef = pkgCfg.getDefaultInterceptorRef();
		
		List<InterceptorMapping> defInterceptorStack = InterceptorBuilder.constructInterceptorReference(
				pkgCfg,
				defaultInterceptorRef,
				Collections.EMPTY_MAP,
				null,
				objectFactory);
		
		return defInterceptorStack;
	}
	
	/**
	 * 渲染ftl页面，如果page action存在，则首先调用
	 * @param namespace
	 * @param actionName
	 * @return
	 */
	public ActionConfig getPageRenderingActionConfig(String namespace, String actionName){
		
		//final String pageLocation = baseDir + namespace + "/"+ actionName + ".ftl";
		//ResultConfig ftlResultCfg = new ResultConfig.Builder("success", FreemarkerResult.class.getName())
		//.addParam("location", pageLocation).build();
		
		String pkgClassName = String.format("%s%s.%sAction", 
				basePackage, 
				namespace.replace('/', '.').toLowerCase(), 
				StringUtils.capitalize(namespace.substring(1)));
		
		Class<?> actionClass = ActionSupport.class; // ActionSupport as default
		String methodName = "execute";
		try {
			Class<?> pkgActionClass = Class.forName(pkgClassName); // try to find package action
			pkgActionClass.getMethod(actionName, new Class<?>[]{}); // and the package method
			
			// if package action & package method are found, assign it
			actionClass = pkgActionClass;
			methodName = actionName;
		} catch (Throwable e) {
			// 没有找到package method, 直接渲染ftl页面
			LOG.debug("package method [{}] not found, render ftl", actionName);
			return getDirectRenderingActionConfig(namespace, actionName);
			
		}
		
		ActionConfig config = new ActionConfig.Builder(namespace, "*", actionClass.getName())
		.methodName(methodName)
		.addInterceptors(getDefaultIntercepterStack(parentPackage))
		//.addResultConfig(ftlResultCfg)
		.build();
		
		return config;
	}
	
	/**
	 * 直接渲染ftl页面的ActionConfig
	 * 按照规则渲染freemarker页面
	 * 规则如下：${baseDir}/${namespace}/${page}.ftl
	 * @param namespace
	 * @return
	 */
	public ActionConfig getDirectRenderingActionConfig(String namespace, String actionName){
		
		if(namespace.equals("/")) namespace = "";
		// freemarker result
		final String pageLocation = baseDir + namespace + "/"+ actionName + ".ftl";
		ResultConfig genericFreemarkerResult = new ResultConfig.Builder("success", FreemarkerResult.class.getName())
		.addParam("location", pageLocation).build();
		
		ActionConfig config = new ActionConfig.Builder(namespace, "*", ActionSupport.class.getName())//.build();
		.addResultConfig(genericFreemarkerResult).build();
		return config;
	}
	
	/**
	 * TODO: cache
	 * @param className
	 * @return
	 */
	public boolean isRedworkAction(String className){
		try {
			Class<?> actionClazz = Class.forName(className);
			Class<?>[] interfaces = actionClazz.getInterfaces();
			for(Class<?> i:interfaces){
				if(i == RedworkAction.class){
					return true;
				}
			}
			return false;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	// 是否Ajax验证
	protected boolean requireJson() {
		HttpServletRequest request = ServletActionContext.getRequest();
		
		// 请求头Accept为json
		String accept = request.getHeader("Accept");
		boolean acceptJson = accept != null && accept.indexOf("application/json") != -1;
		
		// 请求参数指定为json验证
		String jsonFormatStr = request.getParameter("format");
		boolean jsonFormat = jsonFormatStr != null && "json".equals(jsonFormatStr);
		
		return acceptJson || jsonFormat;
	}
}
